package czh.cocoa.ui.impl;

import android.content.MutableContextWrapper;
import czh.cocoa.foundation.NSArray;
import czh.cocoa.foundation.NSIndexPath;
import czh.cocoa.foundation.NSMutableArray;
import czh.cocoa.foundation.NSMutableDictionary;
import czh.cocoa.foundation.NSNumber;
import czh.cocoa.foundation.NSObject;
import czh.cocoa.foundation.NSSet;
import czh.cocoa.foundation.NSString;
import czh.cocoa.graphics.CGContext;
import czh.cocoa.graphics.CGPoint;
import czh.cocoa.graphics.CGRect;
import czh.cocoa.ui.UIColor;
import czh.cocoa.ui.UIEvent;
import czh.cocoa.ui.UIFont;
import czh.cocoa.ui.UIImage;
import czh.cocoa.ui.UITouch;
import czh.cocoa.ui.UIView;

import static java.lang.Math.*;
import static czh.cocoa.graphics.CGContext.CGContextFillRect;
import static czh.cocoa.graphics.CGContext.CGContextSetFillColor;
import static czh.cocoa.ui.UIKit.UIGraphicsBeginImageContext;
import static czh.cocoa.ui.UIKit.UIGraphicsEndImageContext;
import static czh.cocoa.ui.UIKit.UIGraphicsGetCurrentContext;
import static czh.cocoa.ui.UIKit.UIGraphicsGetImageFromCurrentImageContext;
import static czh.cocoa.ui.impl.UITableViewCell.*;

public class UITableView extends UIScrollView {

	public final static int UITableViewStylePlain = 0;
	
	public final static int UITableViewStyleGrouped = 1;
	
	private final static float TABLE_GROUP_PADDING = 15;
	
	private static final int DEFAULT_HEADER_HEIGHT = 46;
		
	private int mStyle;
	
	private UITableViewDataSource mDataSource;
	
	private UITableViewDelegate mDelegate;
	
	private float mRowHeight;
	
	private NSMutableArray mVisibleIndexPaths;
	
	private NSMutableArray mVisibleCells;
	
	private NSMutableArray mReusableCells;
	
	private NSMutableDictionary mCellOffYCaches;
	
	private NSMutableDictionary mCellHeightCaches;
	
	private int mSeparatorStyle;
	
	private UIColor mSeparatorColor;
	
	private NSIndexPath mOnTouchIndexPath;
	
	private NSIndexPath mSelectedIndexPath;
	
	private UIView mTableHeaderView;
	
	private UIView mTableFooterView;
	
	public UITableView() {
		setStyle(UITableViewStylePlain);
	}
	
	public UITableView(CGRect frame) {
		this(frame, UITableViewStylePlain);
	}
	
	public UITableView(CGRect frame, int style) {
		super(frame);
		setStyle(style);
	}
	
	protected void init() {
		super.init();
		mRowHeight = 80;
		mVisibleIndexPaths = new NSMutableArray();
		mVisibleCells = new NSMutableArray();
		mReusableCells = new NSMutableArray();
		mCellOffYCaches = new NSMutableDictionary();
		mCellHeightCaches = new NSMutableDictionary();
		mSeparatorStyle = UITableViewCellSeparatorStyleSingleLine;
		mSeparatorColor = UIColor.grayColor();
	}
	
	public int style() {
		return mStyle;
	}
	
	private void setStyle(int style) {
		mStyle = style;
		if (style == UITableViewStyleGrouped) {
			initGroupedBackgroundColor();
		}
	}
	
	public void setSeparatorStyle(int style) {
		mSeparatorStyle = style;
	}
	
	public int separatorStyle() {
		return mSeparatorStyle;
	}
	
	private void initGroupedBackgroundColor() {
		UIGraphicsBeginImageContext(CGSizeMake(14, 10));
		CGContext ctx = UIGraphicsGetCurrentContext();
		CGContextSetFillColor(ctx, new float[]{197/255f,204/255f,212/255f}); 
		CGContextFillRect(ctx, CGRectMake(0, 0, 14, 10));
		CGContextSetFillColor(ctx, new float[]{203/255f,210/255f,216/255f}); 
		CGContextFillRect(ctx, CGRectMake(0, 0, 4, 10));
		UIImage image = UIGraphicsGetImageFromCurrentImageContext();
		UIGraphicsEndImageContext();
		UIColor color = new UIColor(image);
		setBackgroundColor(color); 
	}
	
	public void setDataSource(UITableViewDataSource ds) {
		mDataSource = ds;
	}
	
	public UITableViewDataSource dataSource() {
		return mDataSource;
	}
	
	public void setDelegate(UITableViewDelegate delegate) {
		mDelegate = delegate;
		super.setDelegate(delegate); 
	}
	
	public UITableViewDelegate delegate() {
		return mDelegate;
	}
	
	public void setRowHeight(float height) {
		mRowHeight = height;
	}
	
	public float rowHeight() {
		return mRowHeight;
	}
	
	private void calculateContentSize() {
		float content_width = bounds().size.width;
		float content_height = 0;
		
		mCellHeightCaches.removeAllObjects();
		mCellOffYCaches.removeAllObjects();
		
		if (mTableHeaderView != null) { 
			content_height += mTableHeaderView.frame().size.height;
		}
		
		if (mDataSource != null) {
			
			int sections = mDataSource.numberOfSectionsInTableView(this);
			for (int i = 0; i < sections; i++) {
				
				int rows = mDataSource.tableViewNumberOfRowsInSection(this, i);
				
				if (rows > 0 && mStyle == UITableViewStyleGrouped) { 
					if (i == 0) {
						content_height += TABLE_GROUP_PADDING;
					} else {
						content_height += TABLE_GROUP_PADDING * 2;
					}
				}
				
				NSIndexPath index_path = NSIndexPath.indexPathForRowInSection(-1, i);
				float header_height = heightForHeaderInSection(i);
				
				mCellHeightCaches.setFloatForKey(header_height, index_path);
				mCellOffYCaches.setFloatForKey(content_height, index_path);
				
				content_height += header_height;
				
				for (int j = 0; j < rows; j++) {
					index_path = NSIndexPath.indexPathForRowInSection(j, i);
					float row_height = heightForRowAtIndexPath(index_path);
					
					mCellHeightCaches.setFloatForKey(row_height, index_path);
					mCellOffYCaches.setFloatForKey(content_height, index_path);
					
					content_height += row_height;
				}
			}
			
			if (mStyle == UITableViewStyleGrouped) { 
				content_height += TABLE_GROUP_PADDING;
			}
			
		} 
		
		if (mTableFooterView != null) { 
			CGRect frame = mTableFooterView.frame();
			frame.origin.y = content_height;
			mTableFooterView.setFrame(frame);
			
			content_height += frame.size.height;
		}
		
		setContentSize(CGSizeMake(content_width, content_height)); 
	}
	
	public void reloadData() {
		mVisibleIndexPaths.removeAllObjects();
		
		for (NSObject o : mVisibleCells) {
			UIView cell = (UIView) o;
			cell.removeFromSuperview();
		}
		mVisibleCells.removeAllObjects();
		
		calculateContentSize();
		
		updateTableViewCells(null);
	}
	
	public void reloadRowsAtIndexPathsWithRowAnimation(NSArray indexPaths, int animation) {
		calculateContentSize();
		updateTableViewCells(indexPaths);
	}
	
	//additional
	public void reloadRowAtIndexPath(NSIndexPath indexPath) {
		NSArray indexPaths = NSArray.arrayWithObjects(indexPath);
		reloadRowsAtIndexPathsWithRowAnimation(indexPaths, 0); 
	}
	
	private void updateTableViewCells(NSArray reloadIndexPaths) {
		if (mDataSource != null) {
			//NSArray visibleIndexPaths = new NSArray(mVisibleIndexPaths);
			
			//NSLog("1. current indexes: " + indexesToStr(mVisibleIndexPaths)); 
			
			float offy = (float) floor(contentOffset().y);
			
			NSArray v_indexs = visibleIndexPathsForOffset(offy);
			NSArray insec_indexs = insectionIndexPathsBetween(v_indexs, mVisibleIndexPaths);
			NSMutableArray v_upt_indexs = differentIndexPathsBetween(v_indexs, insec_indexs);
			NSMutableArray reused_indexs = differentIndexPathsBetween(mVisibleIndexPaths, insec_indexs);
			 
//			NSLog("original indexes: " + indexesToStr(mVisibleIndexPaths)); 
//			NSLog("visible indexes: " + indexesToStr(v_indexs)); 
//			NSLog("update indexes: " + indexesToStr(v_upt_indexs));
//			NSLog("reused indexes: " + indexesToStr(reused_indexs)); 
			
			int upt_count = v_upt_indexs.count();
			
			if (upt_count > 0 || (reloadIndexPaths != null && reloadIndexPaths.count() > 0)) {
				
				if (reloadIndexPaths != null) { 
					for (NSObject o : reloadIndexPaths) { 
						if (!v_upt_indexs.containsObject(o)) { 
							reused_indexs.addObject(o);
							v_upt_indexs.addObject(o);
							upt_count++;
						}
					}
				}
				
				//if (upt_count > 0) { 
					mReusableCells.removeAllObjects();
					
					for (int i = 0; i < reused_indexs.count(); i++) {
						NSIndexPath index_path = (NSIndexPath) reused_indexs.objectAtIndex(i);
						int index = mVisibleIndexPaths.indexOfObject(index_path);
						UIView cell = (UIView) mVisibleCells.objectAtIndex(index);
						mReusableCells.addObject(cell);
						mVisibleCells.removeObjectAtIndex(index);
						mVisibleIndexPaths.removeObjectAtIndex(index); 
					}
				//}
				
				final float cell_width = bounds().size.width;
				
				final boolean grouped = (mStyle == UITableViewStyleGrouped);
				
				
				for (int i = 0; i < upt_count; i++) {
					NSIndexPath index_path = (NSIndexPath) v_upt_indexs.objectAtIndex(i);
					
					UIView view = null;
					
					float cell_y = mCellOffYCaches.floatForKey(index_path);
					float cell_height = mCellHeightCaches.floatForKey(index_path);
					
					boolean is_header = index_path.row() == -1;
					int section = index_path.section();
					
					if (is_header) { 
						if (mDelegate != null) {
							view = mDelegate.tableViewViewForHeaderInSection(this, section);
						}
						if (view == null) {
							NSString header_title = mDataSource.tableViewTitleForHeaderInSection(this, section);
							if (header_title != null) { 
								view = buildDefaultHeaderView(header_title);
							}
						}
					} else {
						
						view = mDataSource.tableViewCellForRowAtIndexPath(this, index_path);
						
					}
					
					if (view != null) {
						
						view.setFrame(CGRectMake(0, cell_y, cell_width, cell_height));
						
						if (view.superview() != this) {
							insertSubviewAtIndex(view, 0);
						}
						
						if (!is_header) {
							UITableViewCell cell = (UITableViewCell) view;
							cell.setGroupedPadding(grouped ? TABLE_GROUP_PADDING : 0); 
							cell.layoutSubviewsInCell();
							
							if (grouped) {
								updateGroupedCellBackground(cell, index_path);  
								cell.setSeparatorStyleWithColor(UITableViewCellSeparatorStyleNone, null);
							} else {
								if (isCellSeparatorLineRequired(index_path)) { 
									cell.setSeparatorStyleWithColor(mSeparatorStyle, mSeparatorColor);
								} else {
									cell.setSeparatorStyleWithColor(UITableViewCellSeparatorStyleNone, null);
								}
							}
							
							if (index_path.isEqual(mSelectedIndexPath) || 
									index_path.isEqual(mOnTouchIndexPath)) {   
								cell.setSelected(true);
							} else {
								cell.setSelected(false);
							}
						}
						
						mVisibleCells.addObject(view);
						mVisibleIndexPaths.addObject(index_path);
						
					}

				} 
				
				for (int i = 0; i < mReusableCells.count(); i++) {
					UIView cell = (UIView) mReusableCells.objectAtIndex(i);
					cell.removeFromSuperview();
				} 
				
//				NSLog("original indexes: " + indexesToStr(visibleIndexPaths)); 
//				NSLog("current indexes: " + indexesToStr(mVisibleIndexPaths)); 
//				NSLog("visible indexes: " + indexesToStr(v_indexs)); 
//				NSLog("update indexes: " + indexesToStr(v_upt_indexs));
//				NSLog("reused indexes: " + indexesToStr(reused_indexs)); 
				
				//NSLog("subviews count: " + subviews().count());
			}
			
			//NSLog("2. current indexes: " + indexesToStr(mVisibleIndexPaths)); 
						
		}
	}
	
	private boolean isCellSeparatorLineRequired(NSIndexPath indexPath) {
		int section = indexPath.section();
		boolean not_required = section != mDataSource.numberOfSectionsInTableView(this) -1 &&
		indexPath.row() == mDataSource.tableViewNumberOfRowsInSection(this, section) - 1 
			&& heightForHeaderInSection(section + 1) > 0;
		return !not_required;
	}
	
	private UIView buildDefaultHeaderView(NSString title) { 
		UIView header = new UIView();
		
		boolean grouped = (mStyle == UITableViewStyleGrouped);
		
		if (!grouped) {
			UIImageView background = new UIImageView();
			background.setImage(UIImage.imageNamed(NSTR("cocoa/table_section_header_background"))); 
			background.setAutoresizingMask(UIViewAutoresizingFlexibleSize);
			header.addSubview(background);
		}
		
		UILabel label = new UILabel(CGRectMake(10, 0, 0, 0)); 
		
		if (grouped) { 
			label.setFrame(CGRectMake(TABLE_GROUP_PADDING + DEFAULT_TEXT_INDENTATION, 0, 0, 0)); 
			label.setTextColor(new UIColor(76/255f, 86/255f, 108/255f, 1));
			label.setFont(UIFont.boldSystemFontOfSize(23));
		} else {
			label.setFrame(CGRectMake(DEFAULT_TEXT_INDENTATION, 0, 0, 0));
			label.setTextColor(UIColor.whiteColor());
			label.setFont(UIFont.boldSystemFontOfSize(20));
		}
		
		label.setText(title);
		label.setAutoresizingMask(UIViewAutoresizingFlexibleSize);
		header.addSubview(label);
		
		return header;
	}

	public UITableViewCell dequeueReusableCellWithIdentifier(NSString identifier) {
		for (int i = 0; i < mReusableCells.count(); i++) {
			UIView view = (UIView) mReusableCells.objectAtIndex(i);
			if (view instanceof UITableViewCell) {
				UITableViewCell cell = (UITableViewCell) view;
				NSString identifier0 = cell.reuseIdentifier();
				if (identifier0 == null && identifier == null ||
						identifier0 != null && identifier0.isEqual(identifier)) {
					mReusableCells.removeObjectAtIndex(i);
					return cell;
				}
			}
		}
		return null;
	}
	
	private NSArray visibleIndexPathsForOffset(float offset) {
		NSMutableArray index_paths = new NSMutableArray();
		
		int sections = mDataSource.numberOfSectionsInTableView(this);
		//float total_height = 0;
		float bounds_height = bounds().size.height;
		
		for (int i = 0; i < sections; i++) {
			int rows = mDataSource.tableViewNumberOfRowsInSection(this, i);
			for (int j = -1; j < rows; j++) {
				NSIndexPath index_path = NSIndexPath.indexPathForRowInSection(j, i);
				float row_height = mCellHeightCaches.floatForKey(index_path);
				float offy = mCellOffYCaches.floatForKey(index_path); 
				if (offy > offset - row_height && offy < offset + bounds_height) {
					index_paths.addObject(index_path);
				}
				//total_height += row_height;
			}
		}
		
		return index_paths;
	}
	
	private NSArray insectionIndexPathsBetween(NSArray indexPaths, NSArray indexPaths2) {
		NSMutableArray insections = new NSMutableArray();
		for (int i = 0; i < indexPaths.count(); i++) {
			NSIndexPath index_path = (NSIndexPath) indexPaths.objectAtIndex(i);
			if (indexPaths2.containsObject(index_path)) {
				insections.addObject(index_path);
			}
		}
		return insections;
	}
	
	private NSMutableArray differentIndexPathsBetween(NSArray indexPaths, NSArray indexPaths2) {
		NSMutableArray diffs = new NSMutableArray();
		for (int i = 0; i < indexPaths.count(); i++) {
			NSIndexPath index_path = (NSIndexPath) indexPaths.objectAtIndex(i);
			if (!indexPaths2.containsObject(index_path)) {
				diffs.addObject(index_path);
			}
		}
		return diffs;
	}
	
	private float heightForRowAtIndexPath(NSIndexPath indexPath) {
		if (mRowHeight > 0) {
			return mRowHeight;
		} else if(mDelegate != null) {
			return mDelegate.tableViewHeightForRowAtIndexPath(this, indexPath);
		}
		return 0;
	}
	
//	private boolean headerExistsInSection(int section) {
//		UIView header_view = null;
//		if (mDelegate != null) {
//			header_view = mDelegate.tableViewViewForHeaderInSection(this, section);
//			if (header_view != null) {
//				return true;
//			}
//		}
//		NSString header_title = mDataSource.tableViewTitleForHeaderInSection(this, section);
//		return header_title != null;
//	}
	
	private float heightForHeaderInSection(int section) {
		if (mDelegate != null) {
			float height = mDelegate.tableViewHeightForHeaderInSection(this, section);
			if (height > 0) {
				return height;
			}
		}
		NSString title = mDataSource.tableViewTitleForHeaderInSection(this, section);
		return (title != null) ? DEFAULT_HEADER_HEIGHT : 0;
	}
	
	protected void scrollViewDidScroll() {
		updateTableViewCells(null);
		super.scrollViewDidScroll();
	}
	
	public NSIndexPath indexPathForCell(UITableViewCell cell) {
		for (int i = 0; i < mVisibleCells.count(); i++) {
			UIView cell0 = (UIView) mVisibleCells.objectAtIndex(i); 
			if (cell0.isEqual(cell)) {  
				return (NSIndexPath) mVisibleIndexPaths.objectAtIndex(i);
			}
		}
		return null;
	}
	
	public UITableViewCell cellForRowAtIndexPath(NSIndexPath indexPath) {
		for (int i = 0; i < mVisibleIndexPaths.count(); i++) {
			NSIndexPath index_path = (NSIndexPath) mVisibleIndexPaths.objectAtIndex(i); 
			if (index_path.isEqual(indexPath)) {  
				return (UITableViewCell) mVisibleCells.objectAtIndex(i);
			}
		}
		return null;
	}
	
	public void deselectRowAtIndexPathAnimated(NSIndexPath indexPath, boolean animated) {
		UITableViewCell cell = cellForRowAtIndexPath(indexPath);
		if (cell != null) { 
			cell.setSelected(false);
			if (indexPath.equals(mSelectedIndexPath)) {  
				mSelectedIndexPath = null;
			}
		}
	}
	
	public void touchesBeganWithEvent(NSSet touches, UIEvent event) {
		if (mDataSource != null) {
			UITouch touch = event.firstTouch();
			CGPoint location = touch.locationInView(this); 
			CGPoint offset = contentOffset();
			location.y += offset.y;
			
			int sections = mDataSource.numberOfSectionsInTableView(this);
			//float total_height = 0;
			
			boolean done = false;
			
			for (int i = 0; i < sections; i++) {
				int rows = mDataSource.tableViewNumberOfRowsInSection(this, i);
				for (int j = -1; j < rows; j++) {
					NSIndexPath index_path = NSIndexPath.indexPathForRowInSection(j, i);
					float row_height = mCellHeightCaches.floatForKey(index_path);
					float offy = mCellOffYCaches.floatForKey(index_path);
					if (location.y > offy && location.y < offy + row_height) { 
						if (j >= 0) {
							UITableViewCell cell = cellForRowAtIndexPath(index_path);
							if (cell != null) { 
								cell.setSelected(true);
								mOnTouchIndexPath = index_path;
							}
						}
						done = true;
						break;
					}
					//total_height += row_height;
				}
				if (done) {
					break;
				}
			}
			
		}
		
	}
	
	public void touchesMovedWithEvent(NSSet touches, UIEvent event) {
		
	}
	
	public void touchesEndedWithEvent(NSSet touches, UIEvent event) {
		if (mOnTouchIndexPath != null && 
				!mOnTouchIndexPath.isEqual(mSelectedIndexPath)) { 
			
			if (mSelectedIndexPath != null) { 
				deselectRowAtIndexPathAnimated(mSelectedIndexPath, false);
			}
			mSelectedIndexPath = mOnTouchIndexPath;
			mOnTouchIndexPath = null;
			if (mDelegate != null) { 
				mDelegate.tableViewDidSelectRowAtIndexPath(this, mSelectedIndexPath);
			}
			
		}
	}
	
	public void touchesCancelledWithEvent(NSSet touches, UIEvent event) {
		if (mOnTouchIndexPath != null) {
			deselectRowAtIndexPathAnimated(mOnTouchIndexPath, false);
		}
	}
	
	private void updateGroupedCellBackground(UITableViewCell cell, NSIndexPath indexPath) {
		int row = indexPath.row();
		int rows = mDataSource.tableViewNumberOfRowsInSection(this, indexPath.section()); 
		
		if (rows == 1) { 
			cell.setGroupedBackgroundStyle(UITableViewCellGroupedWhole); 
		} else if(row == 0) {
			cell.setGroupedBackgroundStyle(UITableViewCellGroupedTop);
		} else if(row == rows - 1) {
			cell.setGroupedBackgroundStyle(UITableViewCellGroupedBottom);
		} else {
			cell.setGroupedBackgroundStyle(UITableViewCellGroupedMiddle);
		}
		
	}
	
	public void setTableHeaderView(UIView header) {
		if (mTableHeaderView != null) { 
			mTableHeaderView.removeFromSuperview();
		}
		if (header != null) { 
			header.setFrame(CGRectMake(0, 0, bounds().size.width, header.bounds().size.height)); 
			header.setAutoresizingMask(UIViewAutoresizingFlexibleWidth);
			addSubview(header);
		}
		
		mTableHeaderView = header;
		
		if (mVisibleCells.count() > 0) {  //has reloaded data
			reloadData();
		}
	}
	
	public UIView tableHeaderView() {
		return mTableHeaderView;
	}
	
	public void setTableFooterView(UIView footer) {
		if (mTableFooterView != null) { 
			mTableFooterView.removeFromSuperview();
		}
		if (footer != null) { 
			footer.setFrame(CGRectMake(0, 0, bounds().size.width, footer.bounds().size.height)); 
			footer.setAutoresizingMask(UIViewAutoresizingFlexibleWidth);
			addSubview(footer);
		}
		
		mTableFooterView = footer;
		
		if (mVisibleCells.count() > 0) {  //has reloaded data
			reloadData();
		}
	}
	
	public UIView tableFooterView() {
		return mTableFooterView;
	}
	
	@SuppressWarnings("unused")
	private String indexesToStr(NSArray array) {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		for (int i = 0; i < array.count(); i++) {
			NSIndexPath index = (NSIndexPath) array.objectAtIndex(i);
			sb.append(index.row());
			if (i != array.count() - 1) {
				sb.append(", "); 
			}
		}
		sb.append("]");
		return sb.toString();
	}
	
}
